package yolo

import (
	"appengine"
	"appengine/datastore"
	"fmt"
	"time"
)

func init() {
	RegisterCacheableType(UserActivities{})
	RegisterCacheableType(UserCommits{})
}

type User struct {
	Email string
}

func NewUser(email string) *User {
	return &User{email}
}

func AddUser(c appengine.Context, email string) error {
	u := NewUser(email)
	key := u.stringKey(c)
	err := GetCachedObjectInMemory(c, key, u)
	if err == nil {
		// Don't bother hitting the datastore if we have the user in memcache.
		return nil
	}
	return StoreCachedObject(c, key, u, true)
}

func (this *User) Key(c appengine.Context) *datastore.Key {
	return datastore.NewKey(c, "User", this.Email, 0, nil)
}

func (this *User) stringKey(c appengine.Context) string {
	return this.Key(c).Encode()
}

func GetUser(c appengine.Context, email string) (*User, error) {
	u := NewUser(email)
	err := GetCachedObject(c, u.stringKey(c), u)
	return u, err
}

func GetUsers(c appengine.Context, offset, limit int, users interface{}) error {
	q := datastore.NewQuery("User").Offset(offset).Limit(limit)
	_, err := q.GetAll(c, users)
	return err
}

// User/File commits activities ----------------------------------------------
type UserCommits struct {
	Count int64
}

// Parent: User
// Kind: UserCommits
// Key: Filename
func (this* UserCommits) Key(c appengine.Context, email, filename string) *datastore.Key {
	u := NewUser(email)
	return datastore.NewKey(c, "UserCommits", filename, 0, u.Key(c))
}

func IncrementUserCommits(c appengine.Context, email string, files []string) error {
	keys := []string{}
	values := []interface{}{}
	for _, file := range files {
		uc := new(UserCommits)
		uc.Count = 1
		key := uc.Key(c, email, file).Encode()
		created := false
		err := GetOrCreateCachedObject(c, key, uc, &created)
		if err != nil {
			c.Warningf("%s: GetOrCreateCachedObject failed for %s: %s", currentLocation(), key, err.Error())
			return err
		}
		if created {
			continue
		}
		keys = append(keys, key)
		values = append(values, uc)
		err = UpdateCachedObjectInMemory(c, key, uc, func(v interface{}) error {
			uc := v.(*UserCommits)
			uc.Count++
			return nil
		})
		if err != nil {
			c.Warningf("%s: UpdateCachedObjectInMemory failed for %s: %s", currentLocation(), key, err.Error())
			return err
		}
	}
	err := PostDelayedCacheFlushMulti(c, keys, values)
	if err != nil {
		c.Warningf("%s: PostDelayedCacheFlushMulti failed: %s", currentLocation(), err.Error())
	}
	return err
}

func (this *User) GetFileCommitCounts(c appengine.Context) ([]*CountMap, error) {
	q := datastore.NewQuery("UserCommits").Order("-Count").Ancestor(this.Key(c)).Limit(100)
	var counters []UserCommits
	keys, err := q.GetAll(c, &counters)
	if err != nil {
		return nil, err
	}
	counts := make([]*CountMap, 0, len(keys))
	for i, k := range keys {
		counts = append(counts, &CountMap{k.StringID(), counters[i].Count})
	}
	return counts, nil
}

// UsserActivities -----------------------------------------------------------
type UserActivities struct {
	ActivityKeys []*datastore.Key
}

// Parent: User
// Kind: UserActivities
// Key: YYYYMMDD
func (this* UserActivities) Key(c appengine.Context, email string, dt time.Time) *datastore.Key {
	u := NewUser(email)
	return datastore.NewKey(c, "UserActivities", fmt.Sprintf("%s", getYMD(dt)), 0, u.Key(c))
}

func AddUserActivities(c appengine.Context, emails []string, activities []*Activity) error {
	keys := []string{}
	values := []interface{}{}
	for i, a := range activities {
		ua := new(UserActivities)
		email := emails[i]
		key := ua.Key(c, email, a.Time).Encode()
		err := GetOrCreateCachedObject(c, key, ua, nil)
		if err != nil {
			return err
		}
		err = UpdateCachedObjectInMemory(c, key, ua, func(v interface{}) error {
			ua := v.(*UserActivities)
			ua.ActivityKeys = append(ua.ActivityKeys, a.Key(c))
			return nil
		})
		if err != nil {
			return err
		}
		keys = append(keys, key)
		values = append(values, ua)
	}
	err := FlushCachedObjectMulti(c, keys, values)
	if err != nil {
		c.Warningf("%s: FlushCachedObjectMulti failed: %s", currentLocation(), err.Error())
	}
	return err
}
